using System;

namespace RockHopper.Common.Model
{
    public class VdotCalculator
    {
        private readonly EasyIntensityCalculator easyIntensityCalculator;
        private readonly MarathonIntensityCalculator marathonIntensityCalculator;
        private readonly ThresholdIntensityCalculator thresholdIntensityCalculator;
        private readonly IntervalIntensityCalculator intervalIntensityCalculator;
        private readonly RepetitionIntensityCalculator repetitionIntensityCalculator;

        public VdotCalculator()
        {
            easyIntensityCalculator = new EasyIntensityCalculator();
            marathonIntensityCalculator = new MarathonIntensityCalculator();
            thresholdIntensityCalculator = new ThresholdIntensityCalculator();
            intervalIntensityCalculator = new IntervalIntensityCalculator();
            repetitionIntensityCalculator = new RepetitionIntensityCalculator();
        }

        public void Calculate(CalculationContext context)
        {
            context.EasyLower = easyIntensityCalculator.Lower(context.Vdot);
            context.EasyUpper = easyIntensityCalculator.Upper(context.Vdot);
            context.Marathon = marathonIntensityCalculator.Value(context.Vdot);
            context.Threshold = thresholdIntensityCalculator.Value(context.Vdot);
            context.Interval = intervalIntensityCalculator.Value(context.Vdot);
            context.Repetition200 = repetitionIntensityCalculator.TwoHundred(context.Vdot);
            context.Repetition400 = repetitionIntensityCalculator.FourHundred(context.Vdot);
            context.Repetition800 = repetitionIntensityCalculator.EightHundred(context.Vdot);
        }

        public bool RaceTime(double vdot, double distance, out double raceTime)
        {
            const double paceMetrePerMinute = 250.0;
            raceTime = distance / paceMetrePerMinute;
            var prevRaceTime = 0.0;
            var iterations = 0;

            while (iterations < 200 && Math.Abs(prevRaceTime - raceTime) > 0.00001)
            {
                prevRaceTime = raceTime;
                var ft = ((((Vdot.A1) * (Math.Pow(distance, 2)) * (Math.Pow(raceTime, -2))) + ((Vdot.A2) * distance * (Math.Pow(raceTime, -1))) - Vdot.A3) /
                          ((Vdot.B1 * Math.Exp(-Vdot.C1 * raceTime)) + (Vdot.B2 * Math.Exp(-Vdot.C2 * raceTime)) + Vdot.B3)) - vdot;

                var fpt = ((((Vdot.B1 * Math.Exp(-Vdot.C1 * raceTime)) + (Vdot.B2 * Math.Exp(-Vdot.C2 * raceTime)) + Vdot.B3) *
                            (-0.000208 * (Math.Pow(distance, 2)) * (Math.Pow(raceTime, -3))) - ((Vdot.A2) * distance * (Math.Pow(raceTime, -2)))) -
                           (81.8 * ((Vdot.B1) * (Math.Exp(-Vdot.C1 * raceTime)) + (Vdot.B2) * (Math.Exp(-Vdot.C2 * raceTime))))) /
                          Math.Pow(((Vdot.B1 * Math.Exp(-Vdot.C1 * raceTime)) + (Vdot.B2 * Math.Exp(-Vdot.C2 * raceTime)) + Vdot.B3), 2);
                raceTime = prevRaceTime - ft / fpt;
                iterations++;
            }

            return iterations < 200;
        }
    }
}